﻿using System.Collections.Generic;
using UnityEngine;
using XLua;
using Yoozoo.Libs;
using Yoozoo.Managers;
using Yoozoo.Mars.Got.SpriteAnim;
using Random = System.Random;

namespace Yoozoo.Modules.InnerCity
{
    public class CityUnitGroup : IClass
    {
        private List<CityUnit> guardList;
        private List<CityUnit> patrolList;
        private List<CityUnit> personList;

        private Dictionary<int, int> idToIndexGuard;
        private Dictionary<int, int> idToIndexPatrol;
        private Dictionary<int, int> idToIndexPerson;

        private List<CityBuildingTeam> buildTeam;

        public void Initialize(Dictionary<int, CityUnitData> guardInfo, Dictionary<int, CityUnitData> patrolInfo, Dictionary<int, CityUnitData> personInfo)
        {
            this.guardList = new List<CityUnit>(guardInfo.Count);
            this.patrolList = new List<CityUnit>(patrolInfo.Count);
            this.personList = new List<CityUnit>(personInfo.Count);
            this.idToIndexGuard = new Dictionary<int, int>(guardInfo.Count);
            this.idToIndexPatrol = new Dictionary<int, int>(patrolInfo.Count);
            this.idToIndexPerson = new Dictionary<int, int>(personInfo.Count);
            this.buildTeam = new List<CityBuildingTeam>(2);

            foreach (var kvp in guardInfo)
            {
                var spriteObjs = CityUnitMgr.Instance.GetSprite("gta_sniper1", 1);
                if (IsValidList(spriteObjs))
                {
                    var unit = ClassManager.Get<CityUnit>();
                    unit.Initialize(kvp.Key, CityUnitType.Guard, spriteObjs[0], GetUnitActions(CityUnitType.Guard), kvp.Value);

                    this.idToIndexGuard.Add(kvp.Key, this.guardList.Count);
                    this.guardList.Add(unit);
                }
            }
            
            foreach (var kvp in patrolInfo)
            {
                var spriteObjs = CityUnitMgr.Instance.GetSprite("gta_sniper1", 1);
                if (IsValidList(spriteObjs))
                {
                    var unit = ClassManager.Get<CityUnit>();
                    unit.Initialize(kvp.Key, CityUnitType.Patrol, spriteObjs[0], GetUnitActions(CityUnitType.Patrol), kvp.Value);
                    
                    this.idToIndexPatrol.Add(kvp.Key, this.patrolList.Count);
                    this.patrolList.Add(unit);
                }
            }
            
            foreach (var kvp in personInfo)
            {
                var spriteObjs = CityUnitMgr.Instance.GetSprite("gta_sniper1", 1);
                if (IsValidList(spriteObjs))
                {
                    var unit = ClassManager.Get<CityUnit>();
                    unit.Initialize(kvp.Key, CityUnitType.Person, spriteObjs[0], GetUnitActions(CityUnitType.Person), kvp.Value);
                    
                    this.idToIndexPerson.Add(kvp.Key, this.personList.Count);
                    this.personList.Add(unit);
                }
            }
        }

        public void Update()
        {
            foreach (var unit in this.guardList)
                unit.Update();
            
            foreach (var unit in this.patrolList)
                unit.Update();

            foreach (var unit in this.personList)
                unit.Update();
        }

        public void PublishWalkCommand(Dictionary<string, Dictionary<int, List<Vector3>>> commandDict)
        {
            foreach (var kvp in commandDict)
            {
                List<CityUnit> list;
                Dictionary<int, int> dict;
                if (!GetUnitStorage(kvp.Key, out list, out dict))
                    continue;

                foreach (var kvp1 in kvp.Value)
                {
                    var unit = list[dict[kvp1.Key]];
                    unit.Walk(kvp1.Value);
                }
            }
        }

        public Vector3 GetUnitPos(string type, int id)
        {
            List<CityUnit> list;
            Dictionary<int, int> dict;
            if (!GetUnitStorage(type, out list, out dict))
                return Vector3.zero;

            return list[dict[id]].Position;
        }

        public void PublishBuildCommand(LuaTable table)
        {
            // 平均分
            var count = this.personList.Count / (this.buildTeam.Count + 1);

            // 随机person 单位组成小队
            var members = RandomSelector.Select(this.personList, count);
            
            var id = table.Get<int>("id");

            var gridW = table.Get<int>("gridW");
            
            var buildingPos = CityUnitMgr.Instance.ParseIndexToGridPos(table.Get<float[]>("buildPos"), gridW);

            var storagePos = CityUnitMgr.Instance.ParseIndexToGridPos(table.Get<float[]>("storagePos"), gridW);

            var resType = ParseResType(table.Get<float[]>("resType"));

            var extraResPos = ParseBuildTeamPos(table.Get<LuaTable>("extraResPos"), gridW);

            var team = ClassManager.Get<CityBuildingTeam>();
            team.Initialize(id, members, buildingPos, storagePos, resType, extraResPos);
            team.DispatchWork();
            this.buildTeam.Add(team);
        }

        public void PublishDismissBuildCommand(int id)
        {
            if (this.buildTeam == null || this.buildTeam.Count == 0)
                return;

            var freeUnit = new List<CityUnit>(this.personList.Count);
            var removeList = new List<int>(this.buildTeam.Count);
            for (var i = 0; i < this.buildTeam.Count; i++)
            {
                var team = this.buildTeam[i];
                if (team.ID == id)
                {
                    removeList.Add(i);
                    freeUnit.AddRange(team.Dismiss());
                }
            }

            for (var i = removeList.Count - 1; i >= 0; i--)
            {
                var team = this.buildTeam[removeList[i]];
                this.buildTeam.RemoveAt(removeList[i]);
                ClassManager.Free(team);
            }

            if (this.buildTeam.Count == 0)
            {
                PublishCelebrateCommand();
                return;;
            }

            var num = freeUnit.Count / this.buildTeam.Count;
            for (var i = 0; i < this.buildTeam.Count; i++)
            {
                var _unit = new List<CityUnit>(num);
                for (var j = num * i; j < num * (i + 1); j++)
                    _unit.Add(freeUnit[j]);
                
                this.buildTeam[i].AddMember(_unit);
            }
        }

        public void PublishPatrolCommand(int id, List<Vector3> path)
        {
            if (!this.idToIndexPatrol.ContainsKey(id))
                return;

            this.patrolList[this.idToIndexPatrol[id]].Patrol(path);
        }

        public void PublishBuildPosChange(int id, List<Vector2Int> grids)
        {
            foreach (var team in this.buildTeam)
                if (team.ID == id)
                    team.OnBuildPosChange(grids);
        }

        public void PublishStoragePosChange(List<Vector2Int> grids)
        {
            foreach (var team in this.buildTeam)
                team.OnStoragePosChange(grids);
        }

        public void PublishResPosChange(int type, int index, List<Vector2Int> grids)
        {
            foreach (var team in this.buildTeam)
                team.OnResPosChange(type, index, grids);
        }

        public int GetRandomUnit()
        {
            var r = new Random();
            var x = r.Next(3);
            List<CityUnit> units = null;
            switch (x)
            {
                case 0:
                    units = this.guardList;
                    break;
                
                case 1:
                    units = this.patrolList;
                    break;
                
                case 2:
                    units = this.personList;
                    break;
            }

            if (units == null)
                return 0;
            
            var i = r.Next(units.Count);
            return (x + 1) * 100 + units[i].ID;
        }

        public void UnitStopMove(string type, int id)
        {
            List<CityUnit> list;
            Dictionary<int, int> dict;
            if (!GetUnitStorage(type, out list, out dict))
                return;
            
            list[dict[id]].StopMove();
        }

        public void UnitContinueMove(string type, int id)
        {
            List<CityUnit> list;
            Dictionary<int, int> dict;
            if (!GetUnitStorage(type, out list, out dict))
                return;
            
            list[dict[id]].ContinueMove();
        }

        public void OnReset()
        {
            Dispose();
        }

        private bool GetUnitStorage(string type, out List<CityUnit> list, out Dictionary<int, int> dict)
        {
            switch (type)
            {
                case CityUnitType.Guard:
                    list = this.guardList;
                    dict = this.idToIndexGuard;
                    return true;
                
                case CityUnitType.Patrol:
                    list = this.patrolList;
                    dict = this.idToIndexPatrol;
                    return true;
                
                case CityUnitType.Person:
                    list = this.personList;
                    dict = this.idToIndexPerson;
                    return true;
                
                default:
                    list = null;
                    dict = null;
                    return false;
            }
        }

        private bool IsValidList(AnimSprite[] arr)
        {
            return arr != null && arr.Length > 0;
        }

        private List<string> GetUnitActions(string unitType)
        {
            var ret = new List<string>();
            ret.Add("IdleAction");

            if (unitType == CityUnitType.Patrol)
                ret.Add("PatrolAction");

            if (unitType == CityUnitType.Person)
            {
                ret.Add("WalkAction");
                ret.Add("BuildAction");
                ret.Add("TakeObjAction");
                ret.Add("CelebrateAction");
                ret.Add("CollectAction");
            }
            
            return ret;
        }

        private List<int> ParseResType(float[] types)
        {
            if (types == null || types.Length == 0)
                return null;

            var ret = new List<int>(types.Length);
            foreach (var type in types)
                ret.Add((int) type);

            return ret;
        }

        private Dictionary<int, Dictionary<int, List<Vector2Int>>> ParseBuildTeamPos(LuaTable table, int gridW)
        {
            Dictionary<int, Dictionary<int, List<Vector2Int>>> ret = null;
            if (table == null)
                return ret;
            
            var types = table.GetKeys();
            foreach (var type in types)
            {
                var _type = (long) type;
                var _typeTable = table.Get<long, LuaTable>(_type);
                if (_typeTable == null)
                    continue;

                Dictionary<int, List<Vector2Int>> typeDict = null;
                var indexes = _typeTable.GetKeys();
                foreach (var index in indexes)
                {
                    var _index = (long) index;
                    var areaArr = _typeTable.Get<long, float[]>(_index);
                    var posList = CityUnitMgr.Instance.ParseIndexToGridPos(areaArr, gridW);
                    if (posList == null)
                        continue;

                    if (typeDict == null)
                        typeDict = new Dictionary<int, List<Vector2Int>>();

                    typeDict.Add((int) _index, posList);
                }

                if (typeDict != null)
                {
                    if (ret == null)
                        ret = new Dictionary<int, Dictionary<int, List<Vector2Int>>>();

                    ret.Add((int) _type, typeDict);
                }
            }

            return ret;
        }

        private void PublishCelebrateCommand()
        {
            foreach (var unit in this.personList)
                unit.Celebrate();
        }

        private void Dispose()
        {
            foreach (var team in this.buildTeam)
                ClassManager.Free(team);
            
            foreach (var unit in this.guardList)
                ClassManager.Free(unit);

            foreach (var unit in this.patrolList)
                ClassManager.Free(unit);

            foreach (var unit in this.personList)
                ClassManager.Free(unit);
            
            
        }
    }

    public class RandomSelector
    {
        public static List<T> Select<T>(List<T> rawList, int count, Random random = null)
        {
            if (rawList == null || rawList.Count == 0 || rawList.Count <= count)
                return rawList;

            if (random == null)
                random = new Random(rawList.GetHashCode());

            var indexList = new List<int>(rawList.Count);
            for (var i = 0; i < rawList.Count; i++)
                indexList.Add(i);

            var ret = new List<T>(count);
            var randomMark = indexList.Count;
            while (ret.Count < count)
            {
                var index = random.Next(randomMark);
                ret.Add(rawList[indexList[index]]);
                if (ret.Count == count)
                    break;
                
                randomMark--;
                var t = indexList[randomMark];
                indexList[randomMark] = indexList[index];
                indexList[index] = t;
            }

            return ret;
        }
    }
}